import fetch from 'node-fetch';
import {createLogger} from "./Logger.js";


export class OpsClient {
    /**
     * @param {string} vopsToken 认证 token
     * @param {boolean} verbose 打印日志
     */
    constructor(vopsToken, verbose=false) {
        this.vopsToken = vopsToken;
        this.baseUrl = 'https://vops.hszq8.com';
        this.verbose = verbose;
        this.logger = createLogger("HTTP");
    }

    /**
     * 发起请求（带 data）
     * @param {string} pathOrUrl API 路径或完整 URL
     * @param {string} method 请求方法
     * @param {object|null} body 请求体
     * @returns {Promise<any>}
     */
    async request(pathOrUrl, method = 'GET', body = null) {
        const url = pathOrUrl.startsWith('http') ? pathOrUrl : `${this.baseUrl}${pathOrUrl}`;
        const options = {
            method,
            headers: {
                'Accept': 'application/json, text/plain, */*',
                'Cache-Control': 'no-cache',
                'Connection': 'keep-alive',
                'Pragma': 'no-cache',
                'Referer': this.baseUrl,
                'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36',
                'Cookie': `vops_token=${this.vopsToken}`,
            },
        };
        if (body) {
            options.headers['Content-Type'] = 'application/json';
            options.body = JSON.stringify(body);
        }

        if (this.verbose) {
            this.logger.info(
                `➡️ 请求:\n url=${url}\n method=${method}\n headers=${JSON.stringify(options.headers)}\n body=${options.body || '{}'}`
            );
        }

        const res = await fetch(url.toString(), options);

        const resText = await res.text();
        let resData;
        try {
            resData = JSON.parse(resText);
        } catch {
            resData = resText;
        }
        if (this.verbose) {
            this.logger.info(
                `⬅️ 响应:\n url=${url}\n status=${res.status}\n body=${JSON.stringify(resData)}`
            );
        }
        if (!res.ok) {
            this.logger.info(
                `➡️ 请求:\n url=${url}\n method=${method}\n headers=${JSON.stringify(options.headers)}\n body=${options.body || '{}'}`
            );
            this.logger.info(
                `⬅️ 响应:\n url=${url}\n status=${res.status}\n body=${JSON.stringify(resData)}`
            );
            throw new Error(`请求失败，状态码 ${res.status}`);
        }

        if (resData.hasOwnProperty('data')) {
            if (resData.hasOwnProperty('status')) {
                if (resData.status !== 'success') {
                    throw new Error(`请求失败，状态${resData.status} 错误 ${resData.message}`);
                }
                return resData.data;
            }
            if (resData.hasOwnProperty('detail')) {
                throw new Error(`请求失败，错误 ${resData.detail}`);
            }
        }
        return resData;
    }

    /**
     * 调用 Nacos 配置中心相关接口，兼容 status=0 返回结构
     * @param {URL|string} url 请求地址
     * @param {string} method HTTP 方法
     * @returns {Promise<any>}
     */
    async requestNacos(url, method = 'GET') {
        const finalUrl = url.toString();
        const options = {
            method,
            headers: {
                'Accept': 'application/json, text/plain, */*',
                'Cache-Control': 'no-cache',
                'Connection': 'keep-alive',
                'Pragma': 'no-cache',
                'Referer': this.baseUrl,
                'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36',
                'Cookie': `vops_token=${this.vopsToken}`,
            },
        };

        if (this.verbose) {
            this.logger.info(`➡️ 请求:\n url=${finalUrl}\n method=${method}\n headers=${JSON.stringify(options.headers)}`);
        }

        const res = await fetch(finalUrl, options);
        const resText = await res.text();
        let resData;
        try {
            resData = JSON.parse(resText);
        } catch {
            resData = resText;
        }

        if (this.verbose) {
            this.logger.info(`⬅️ 响应:\n url=${finalUrl}\n status=${res.status}\n body=${JSON.stringify(resData)}`);
        }

        if (!res.ok) {
            throw new Error(`请求失败，状态码 ${res.status}`);
        }

        if (!resData || typeof resData !== 'object') {
            throw new Error('配置接口返回为空或格式异常');
        }

        if (resData.status !== 0) {
            const message = resData.message || resData.msg || '配置接口返回异常';
            throw new Error(`${message}（status=${resData.status}）`);
        }

        return resData;
    }

    /**
     * Nacos 接口 POST 请求
     * @param {string} path 路径
     * @param {object} body 请求体
     * @returns {Promise<any>}
     */
    async postNacos(path, body) {
        const url = `${this.baseUrl}${path}`;
        const options = {
            method: 'POST',
            headers: {
                'Accept': 'application/json, text/plain, */*',
                'Cache-Control': 'no-cache',
                'Connection': 'keep-alive',
                'Pragma': 'no-cache',
                'Referer': this.baseUrl,
                'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36',
                'Cookie': `vops_token=${this.vopsToken}`,
                'Content-Type': 'application/json;charset=UTF-8',
            },
            body: JSON.stringify(body || {}),
        };

        if (this.verbose) {
            this.logger.info(`➡️ 请求:\n url=${url}\n method=POST\n headers=${JSON.stringify(options.headers)}\n body=${options.body}`);
        }

        const res = await fetch(url, options);
        const resText = await res.text();
        let resData;
        try {
            resData = JSON.parse(resText);
        } catch {
            resData = resText;
        }

        if (this.verbose) {
            this.logger.info(`⬅️ 响应:\n url=${url}\n status=${res.status}\n body=${JSON.stringify(resData)}`);
        }

        if (!res.ok) {
            throw new Error(`请求失败，状态码 ${res.status}`);
        }
        if (!resData || typeof resData !== 'object') {
            throw new Error('配置接口返回为空或格式异常');
        }
        if (resData.status !== 0) {
            const message = resData.message || resData.msg || '配置接口返回异常';
            throw new Error(`${message}（status=${resData.status}）`);
        }
        return resData;
    }

    /**
     * 搜索 Nacos 配置文件
     * @param {string} clusterAddress nacos 集群地址
     * @param {string} namespace 命名空间
     * @param {string} dataId 配置 dataId
     * @param {number} pageSize 分页大小
     * @param {number} pageNo 页码
     * @returns {Promise<any>}
     */
    async searchNacosConfig(clusterAddress, namespace, dataId, pageSize = 10, pageNo = 1) {
        const url = new URL('/app/api/v1/nacos_config/get_ns_config', this.baseUrl);
        url.searchParams.set('cluster_address', clusterAddress);
        url.searchParams.set('namespace', namespace);
        url.searchParams.set('page_size', String(pageSize));
        url.searchParams.set('page_no', String(pageNo));
        url.searchParams.set('data_id', dataId || '');
        url.searchParams.set('group', '');
        return this.requestNacos(url);
    }

    /**
     * 获取 Nacos 配置文件内容
     * @param {string} clusterAddress nacos 集群地址
     * @param {string} namespace 命名空间
     * @param {string} dataId 配置 dataId
     * @param {string} group 配置 group
     * @returns {Promise<any>}
     */
    async getNacosConfiguration(clusterAddress, namespace, dataId, group = '') {
        const url = new URL('/app/api/v1/nacos_config/get_configuration', this.baseUrl);
        url.searchParams.set('cluster_address', clusterAddress);
        url.searchParams.set('namespace', namespace);
        url.searchParams.set('data_id', dataId || '');
        url.searchParams.set('group', group || '');
        return this.requestNacos(url);
    }

    /**
     * 提交 Nacos 配置变更
     * @param {object} body 请求体
     * @returns {Promise<any>}
     */
    async submitNacosConfigChange(body) {
        return this.postNacos('/app/api/v1/nacos_config/config_submit', body);
    }

    /**
     * 查询 Nacos 变更审批列表
     * @param {string} clusterAddress 集群地址
     * @param {boolean} onlyUser 是否仅查当前用户
     * @returns {Promise<any>}
     */
    async listNacosApprovals(clusterAddress, onlyUser = true) {
        const url = new URL('/app/api/v1/nacos_config_approval/approval_list', this.baseUrl);
        url.searchParams.set('cluster_address', clusterAddress);
        url.searchParams.set('namespace', '');
        url.searchParams.set('approval_status', '');
        url.searchParams.set('op_status', '');
        url.searchParams.set('originator_user', '');
        url.searchParams.set('op_user', '');
        url.searchParams.set('only_user', String(!!onlyUser));
        return this.requestNacos(url);
    }

    /**
     * 发布 Nacos 配置
     * @param {string} approvalId 审批单号
     * @returns {Promise<any>}
     */
    async publishNacosConfiguration(approvalId) {
        return this.postNacos('/app/api/v1/nacos_config/publish_configuration', { approval_id: approvalId });
    }


    /**
     * 查询默认通知人（组长账号）
     * @returns {Promise<string|null>}
     */
    async fetchInformMemberAccount() {
        const data = await this.request('/app/api/v1/apps/inform_member');
        if (typeof data === 'string') {
            return data.trim();
        }
        if (data && typeof data === 'object' && data.account) {
            return String(data.account).trim();
        }
        return null;
    }


    /**
     * 获取普通发布单详情
     * @param {string} flowId
     * @param {string} flowType
     */
    async get_flow_detail(flowId, flowType) {
        return this.request(`/app/api/v1/flow/get_flow_detail?id=${flowId}&type=${flowType}`);
    }
    /**
     * 获取依赖发布单详情
     * @param {string} id
     */
    async releaseInfo(id) {
        return this.request(`/app/api/v1/release/${id}/info`);
    }
    /**
     * 获取依赖发布单项目
     * @param {string} id
     */
    async releaseApp(id) {
        return this.request(`/app/api/v1/release/${id}/app`);
    }
    /**
     * 获取普通发布单详情
     * 获取所有项目
     * @param {string} flowId
     * @param {string} flowType
     */
    async getAppAndBranch(flowId, flowType) {
        return this.request(`/app/api/v1/flow/get_app_and_branch?type=${flowType}&id=${flowId}`);
    }
    /**
     * 获取普通发布单部署详情
     * @param {bigint} flowAppId 发布单 id
     */
    async buildHistory(flowAppId) {
        return this.request(`/app/api/v1/flow/build_history/${flowAppId}`);
    }

    /**
     * 获取依赖发布单详情
     * @param {string} id
     */
    async getDepsRelease(id) {
        return this.request(`/app/release/deps/${id}`);
    }

    /**
     * 获取发布计划详情
     * @param {string} id
     */
    async getPlanRelease(id) {
        return this.request(`/app/flow/plan_detail?id=${id}`);
    }
    /**
     * 创建发布单
     * @param {Object} body 请求体
     * @returns {Promise<any>}
     */
    async createFlow(body) {
        return this.request('/app/api/v1/flow/add', 'POST', body);
    }
    /**
     * 发布单转开发
     * @param {Object} body 请求体
     * @returns {Promise<any>}
     */
    async startDev(body) {
        return this.request('/app/api/v1/flow/start_dev', 'POST', body);
    }
    /**
     * 获取环境信息
     * @param {string} opsName
     */
    async appEnv(opsName) {
        return this.request(`/app/api/v1/app/${opsName}`);
    }
    /**
     * 获取xxx环境下的pod
     * @param {string} opsName 名字
     * @param env 环境信息
     */
    async podV2(opsName,env) {
        return this.request(`/app/api/v1/apps/podv2/${opsName}/${env}`);
    }

    /**
     * 同步 ingress 配置
     * @param {{release:string,gray_version:string,namespace:string}} body
     */
    async syncIngress(body) {
        return this.request('/app/api/v1/apps/sync_to_ingress', 'POST', body);
    }

    /**
     * 获取镜像信息，提交记录等
     * @param {string} version hs_bss_admin_server:20250821-191442.852881
     * @param opsName 名字
     */
    async imageVersion(opsName,version) {
        return this.request(`/app/api/v1/app/${opsName}/image_version?version=${version}`);
    }
    /**
     * 获取分支的提交信息
     * @param {string} branch 分支
     * @param opsName 名字
     */
    async revs(opsName,branch) {
        return this.request(`/app/api/v1/app/${opsName}/revs?branch=${branch}`);
    }
    /**
     * k8s 部署信息
     * @param {string} env hspt-feature 这种
     * @param opsName 名字
     */
    async k8sResource(opsName,env) {
        return this.request(`/app/api/v1/resource/k8s_resource/${opsName}/${env}`);
    }
    /**
     *  部署
     * @param {Object} body 构建参数
     * @param opsName 名字
     */
    async buildApp(opsName,body) {
        return this.request(`/app/api/v1/app/${opsName}/build`,'POST',body);
    }
    /**
     *  部署日志
     * @param opsName 名字
     */
    async appBuildList(opsName) {
        return this.request(`/app/api/v1/app/${opsName}/get_list/builds`);
    }
    /**
     *  部署
     * @param {Object} id 发布单id
     * @param opsName 名字  hs-crm-money-jar
     */
    async buildSupport(opsName,id) {
        return this.request(`/app/api/v1/release/${id}/${opsName}/build`,'PUT');
    }
    /**
     *  创建cr链接
     * @param {Object} appId 项目id
     * @param opsName 名字  hs-crm-money-jar
     */
    async getCrUrl(appId) {
        return this.request(`/app/api/v1/flow/${appId}/get_cr_url`);
    }
    /**
     * 获取发布单的cr链接，不会创建
     * 获取所有项目
     * @param {string} flowId
     * @param {string} flowType
     */
    async getCrData(flowId,flowType) {
        return this.request(`/app/api/v1/flow/get_cr_data?type=${flowType}&id=${flowId}`);
    }
    /**
     *  获取部署进度
     * @param {Object} id 发布单id
     * @param opsName 名字  hs-crm-money-jar
     */
    async getBuildSupportInfo(opsName,id) {
        return this.request(`/app/api/v1/release/${id}/${opsName}/build`);
    }
    /**
     *  发布单项目部署
     * @param flowId 发布单的应用id
     */
    async buildAndDeploy(flowId) {
        return this.request(`/app/api/v1/flow/build_and_deploy/${flowId}`,'POST');
    }
    /**
     *  基座部署
     * @param {Object} body 构建参数
     * @param opsName 基座名字
     */
    async klCreateBuild(opsName,body) {
        return this.request(`/app/api/v1/app/${opsName}/kl_create_build`,'POST',body);
    }

    /**
     * 发布单提测（转 feature testing）
     * @param {Object} body 请求体
     * @returns {Promise<any>}
     */
    async toFeatureTesting(body) {
        return this.request('/app/api/v1/flow/to_feature_testing/', 'POST', body);
    }

    /**
     * 发布单测试通过
     * @param {Object} body 请求体
     * @returns {Promise<any>}
     */
    async featureTestingPassed(body) {
        return this.request('/app/api/v1/flow/feature_testing_passed/', 'POST', body);
    }

    /**
     * 创建发布计划
     * @param {Object} body 请求体
     * @returns {Promise<any>}
     */
    async createFlowPlan(body) {
        return this.request('/app/api/v1/flow_plan/create', 'POST', body);
    }

    /**
     * 发布计划需求确认
     * @param {Object} body 请求体
     * @returns {Promise<any>}
     */
    async confirmFlowPlanRequirement(body) {
        return this.request('/app/api/v1/flow_plan/requirement_confirm', 'POST', body);
    }

}
/**
 *
 */
export function opsClient(token,verbose) {
    return new OpsClient(token,verbose);
}

/**
 * @param url 请求地址
 * @param vops_token
 * @returns {Promise<unknown>}
 */
export async function opsRequest(url, vops_token) {
    const res = await fetch(url.toString(), {
        headers: {
            'Accept': 'application/json, text/plain, */*',
            'Cache-Control': 'no-cache',
            'Connection': 'keep-alive',
            'Pragma': 'no-cache',
            'Referer': 'https://vops.hszq8.com',
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36',
            'Cookie': `vops_token=${vops_token}`,
        },
    });
    if (!res.ok) {
        throw new Error(`请求失败，状态码 ${res.status}`);
    }
    const data = await res.json();
    if (data.detail) {
        throw new Error(`请求失败，错误 ${data.detail}`);
    }
    return data;
}
